/*
 *  socket.cpp
 */

#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include "debug.h"
#include "socket.h"
#include "socket_exception.h"

Socket::Socket(const int port, const char* ipaddr)
{
    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) throw SocketException();
    memset(&sockaddr, 0, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(port);
    if (ipaddr == NULL) {
        sockaddr.sin_addr.s_addr = INADDR_ANY;
    } else {
        inet_pton(AF_INET, ipaddr, &(sockaddr.sin_addr));
    }
}

Socket::Socket(const int sockfd, const sockaddr_in_t* sockaddr)
{
    this->sockfd = sockfd;
    this->sockaddr = *sockaddr;
}

Socket::~Socket()
{
    shutdown(sockfd, SHUT_RDWR);
    close(sockfd);
}

void Socket::bind()
{
    int retval = ::bind(sockfd, (sockaddr_t*) &sockaddr, sizeof(sockaddr));
    if (retval == -1) throw SocketException();
    /*
    int yes = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
    }
    */
}

void Socket::connect()
{
    int retval = ::connect(sockfd, (sockaddr_t*) &sockaddr, sizeof(sockaddr));
    if (retval == -1) throw SocketException();
}

void Socket::listen(int backlog)
{
    int retval = ::listen(sockfd, backlog);
    if (retval == -1) throw SocketException();
}

Socket* Socket::accept()
{
    sockaddr_storage_t addr;
    socklen_t addr_size = sizeof(addr);
    int new_sockfd = ::accept(sockfd, (sockaddr_t*) &addr, &addr_size);
    if (new_sockfd == -1) throw SocketException();
    return new Socket(new_sockfd, (sockaddr_in_t*) &addr);
}

int Socket::send(char* buf, int size) 
{
    int bytes_total = 0;
    int bytes_sent;
    while (bytes_total < size) {
        bytes_sent = ::send(sockfd, buf + bytes_total, size - bytes_total, 0);
        if (bytes_sent < 0) throw SocketException();
        if (bytes_sent == 0) break;
        bytes_total += bytes_sent;
    }
    return bytes_total;
}

int Socket::recv(char* buf, int size)
{
    int bytes_recv = ::recv(sockfd, buf, size, 0);
    PRINT(bytes_recv);
    if (bytes_recv < 0) throw SocketException();
    return bytes_recv;
}

//{+}

